package model

import (
	"context"
	"encoding/json"
	"game_server/dao/areaweight"
	"game_server/dao/barrage"
	"game_server/dao/period"
	"game_server/dao/prize"
	"game_server/dao/room"
	"game_server/dao/settle"
	"game_server/entity"
	"game_server/framework/connection"
	"game_server/framework/global"
	"game_server/framework/message"
	"game_server/proto"
	"game_server/tool"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/duke-git/lancet/v2/convertor"
	datastructure "github.com/duke-git/lancet/v2/datastructure/list"
	"github.com/duke-git/lancet/v2/maputil"
	"github.com/duke-git/lancet/v2/random"
	"github.com/mroth/weightedrand/v2"
	"github.com/redis/go-redis/v9"
	"github.com/spf13/viper"
)

const (
	WAITING_ROBOT  = -1
	WAITING_JOIN   = 0
	WAITING_BET    = 1
	WAITING_SETTLE = 2
	WAITING_SLEEP  = 3
	WAITING_RESET  = 4
)

type RoomInfo struct {
	RoomConfig      *RoomConfig
	AreaInfos       []int
	PlayerInfos     *maputil.ConcurrentMap[int64, *PlayerInfo]
	BetInfos        *maputil.ConcurrentMap[int64, *BetInfo]
	LastKillerAreas *datastructure.List[int]
	Status          int
	Countdown       int
	AreaSettles     map[int]*AreaSettle
	RobotInfos      *maputil.ConcurrentMap[int64, *RobotInfo]
	PrizePoolCoins  int64
	PrizeLock       sync.Mutex
	BarrageInfos    []*Barrage
}

func RoomInfoInitialize(roomConfig *RoomConfig, barrageInfos []*Barrage) *RoomInfo {
	areaSettles := make(map[int]*AreaSettle)
	for _, v := range AreaTypes {
		areaSettles[v] = &AreaSettle{
			Type:           v,
			Weight:         10000,
			PlayerBetCoins: int64(0),
			RobotBetCoins:  int64(0),
		}
	}
	roomInfo := RoomInfo{
		RoomConfig:      roomConfig,
		AreaInfos:       AreaTypes,
		PlayerInfos:     maputil.NewConcurrentMap[int64, *PlayerInfo](0),
		BetInfos:        maputil.NewConcurrentMap[int64, *BetInfo](0),
		LastKillerAreas: datastructure.NewList([]int{}),
		Status:          WAITING_ROBOT,
		Countdown:       60,
		AreaSettles:     areaSettles,
		RobotInfos:      maputil.NewConcurrentMap[int64, *RobotInfo](0),
		PrizePoolCoins:  roomConfig.PrizePoolAmount,
		BarrageInfos:    barrageInfos,
	}
	go roomInfo.GameStart()
	go roomInfo.PrizeManualSignal()
	go roomInfo.barrageLoop()
	return &roomInfo
}

func (r *RoomInfo) SynRoomInfo() {
	var senderIds []int64
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			return true
		}
		senderIds = append(senderIds, k)
		return true
	})
	playerCount := int64(0)
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		playerCount++
		return true
	})
	connectionWrappers := connection.ConnectionManagerInstance().QueryConnMultiple(senderIds)
	for _, v := range connectionWrappers {
		if v == nil {
			continue
		}
		messageWrapper := message.MessageWrapper{
			Code: proto.ROOMINFO,
			Data: proto.RoomInfoRes{
				Periods:         r.RoomConfig.Periods,
				RoomLevel:       r.RoomConfig.RoomLevel,
				AreaInfos:       r.AreaInfos,
				LastKillerAreas: r.LastKillerAreas.Data(),
				Status:          r.Status,
				Countdown:       r.Countdown,
				Chips:           r.RoomConfig.Chips,
				StartCount:      r.RoomConfig.RoomStartCount,
				TotalCount:      r.RoomConfig.RoomTotalCount,
				CurrentCount:    playerCount,
				BarrageState:    r.RoomConfig.BarrageState,
			},
		}
		v.Receive(&messageWrapper)
	}
}

func (r *RoomInfo) SynPlayerInfo() {
	var senderIds []int64
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			return true
		}
		senderIds = append(senderIds, k)
		return true
	})
	connectionWrappers := connection.ConnectionManagerInstance().QueryConnMultiple(senderIds)
	for _, v := range connectionWrappers {
		if v == nil {
			continue
		}
		playerInfos := make([]proto.PlayerInfoRes, 0)
		r.PlayerInfos.Range(func(_ int64, item *PlayerInfo) bool {
			playerInfo := proto.PlayerInfoRes{}
			err := convertor.CopyProperties(&playerInfo, item)
			if err != nil {
				global.Logger.Errorf("err:%v", err)
			}
			if playerInfo.Id == v.UserId {
				playerInfo.IsSelf = true
			} else {
				playerInfo.IsSelf = false
				if len(playerInfo.NickName) > 2 {
					playerInfo.NickName = playerInfo.NickName[0:1] + "**"
				}
			}
			playerInfos = append(playerInfos, playerInfo)
			return true
		})
		messageWrapper := message.MessageWrapper{
			Code: proto.PLAYERINFO,
			Data: playerInfos,
		}
		v.Receive(&messageWrapper)
	}
}

func (r *RoomInfo) SynBetInfo() {
	var senderIds []int64
	betInfos := make([]proto.BetInfoRes, 0)
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			return true
		}
		senderIds = append(senderIds, k)
		return true
	})
	r.BetInfos.Range(func(k int64, v *BetInfo) bool {
		betInfo := proto.BetInfoRes{}
		err := convertor.CopyProperties(&betInfo, v)
		if err != nil {
			global.Logger.Errorf("err:%v", err)
		}
		betInfos = append(betInfos, betInfo)
		return true
	})
	connectionWrappers := connection.ConnectionManagerInstance().QueryConnMultiple(senderIds)
	for _, v := range connectionWrappers {
		if v == nil {
			continue
		}
		messageWrapper := message.MessageWrapper{
			Code: proto.BETINFO,
			Data: betInfos,
		}
		v.Receive(&messageWrapper)
	}
}

func (r *RoomInfo) SynPrizeInfo() {
	global.Logger.Infof("房间:%v,当前奖池:%v", r.RoomConfig.RoomLevel, r.PrizePoolCoins)
	var senderIds []int64
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			return true
		}
		senderIds = append(senderIds, k)
		return true
	})
	connectionWrappers := connection.ConnectionManagerInstance().QueryConnMultiple(senderIds)
	for _, v := range connectionWrappers {
		if v == nil {
			continue
		}
		prizePoolRes := proto.PrizePoolRes{
			PrizeCoins: r.PrizePoolCoins,
			MostOpen:   false,
		}
		if r.PrizePoolCoins >= r.RoomConfig.AutoEruptAmount*90/100 {
			prizePoolRes.MostOpen = true
		}
		messageWrapper := message.MessageWrapper{
			Code: proto.PrizePoolInfo,
			Data: prizePoolRes,
		}
		v.Receive(&messageWrapper)
	}
}

func (r *RoomInfo) GameStart() {
	for {
		switch r.Status {
		case WAITING_ROBOT:
			r.generateRobot()
			r.robotComplete()
			r.Status = WAITING_JOIN
		case WAITING_JOIN:
			r.gameJoin()
			break
		case WAITING_BET:
			r.gameBet()
			break
		case WAITING_SETTLE:
			r.gameSettle()
			break
		case WAITING_SLEEP:
			time.Sleep(30 * time.Second)
			r.Status = WAITING_RESET
			break
		case WAITING_RESET:
			r.gameReset()
			break
		default:
			global.Logger.Infof("房间LEVEL:%v,当前状态错误:%v", r.RoomConfig.RoomLevel, r.Status)
		}
	}
}

func (r *RoomInfo) gameJoin() {
	playerCount := int64(0)
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		playerCount++
		return true
	})
	if playerCount >= r.RoomConfig.RoomStartCount {
		r.Status = WAITING_BET
	}
}

func (r *RoomInfo) gameBet() {
	if r.Countdown < 0 {
		r.Status = WAITING_SETTLE
	} else {
		r.robotBet()
		r.robotInOrOut()
		r.SynRoomInfo()
		r.SynBetInfo()
		r.SynPlayerInfo()
		time.Sleep(1 * time.Second)
		r.Countdown--
	}
}

func (r *RoomInfo) gameSettle() {
	defer r.PrizeLock.Unlock()
	r.PrizeLock.Lock()
	global.Logger.Infof("房间LEVEL:%v, 游戏结算", r.RoomConfig.RoomLevel)
	startTimestamp := tool.CurrentTimestamp()
	r.killerStrategy()
	endTimestamp := tool.CurrentTimestamp()
	global.Logger.Infof("花费时间:%v", (endTimestamp-startTimestamp)/1000)
	r.prizePoolIncrease()
	r.prizeSettle()
	r.SynPrizeInfo()
	gameRoom := &entity.GameRoomEntity{
		Id:              r.RoomConfig.Id,
		RoomGainLoss:    r.RoomConfig.RoomGainLoss,
		RoomTax:         r.RoomConfig.RoomTax,
		Periods:         r.RoomConfig.Periods + 1,
		PrizePoolAmount: r.PrizePoolCoins,
	}
	room.UpdateById(gameRoom)
	r.SynRoomInfo()
	r.updatePlayerWeight()
	global.Logger.Infof("房间LEVEL:%v, 游戏重置", r.RoomConfig.RoomLevel)
	r.BetInfos = maputil.NewConcurrentMap[int64, *BetInfo](0)
	for _, v := range r.AreaSettles {
		v.reset()
	}
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			return true
		}
		if !v.IsGaming {
			return true
		}
		v.IsGaming = false
		toJson, _ := convertor.ToJson(v)
		tokenTime := viper.GetInt("redis.token-time")
		global.RedisClient.Set(context.Background(), viper.GetString("redis.player-key")+convertor.ToString(v.Id), toJson, time.Duration(tokenTime)*time.Minute)
		return true
	})
	r.reloadRoomConfig()
	r.Countdown = 60
	r.Status = WAITING_SLEEP
}

func (r *RoomInfo) updatePlayerWeight() {
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			return true
		}
		redisKey := viper.GetString("redis.weight-key") + convertor.ToString(v.Id)
		result, _ := global.RedisClient.Exists(context.Background(), redisKey).Result()
		if result > 0 {
			weightInfo := make(map[string]float64)
			data, _ := global.RedisClient.Get(context.Background(), redisKey).Result()
			err := json.Unmarshal([]byte(data), &weightInfo)
			if err != nil {
				global.Logger.Errorf("err:%v", err)
			}
			weight, err := convertor.ToInt(weightInfo["winPercent"])
			if err != nil {
				global.Logger.Infof("err:%v", err)
			}
			v.WinPercent = weight
			global.RedisClient.Del(context.Background(), redisKey)
			global.Logger.Infof("更新房间%v, 玩家:%v, 胜率%v", r.RoomConfig.RoomLevel, v.NickName, v.WinPercent)
		}
		return true
	})
}

func (r *RoomInfo) gameReset() {
	r.robotPartExitRoom()
	r.barrageReset()
	r.Status = WAITING_ROBOT
}

func (r *RoomInfo) reloadRoomConfig() {
	roomParam := &entity.GameRoomEntity{
		Status:    1,
		RoomLevel: r.RoomConfig.RoomLevel,
		GameType:  r.RoomConfig.GameType,
	}
	result := room.QueryByParams(roomParam, 1, 1)
	roomConfig := RoomConfig{}
	err := convertor.CopyProperties(&roomConfig, &result[0])
	if err != nil {
		global.Logger.Errorf("err:%v", err)
	}
	r.RoomConfig = &roomConfig
}

func (r *RoomInfo) killerStrategy() {
	settleTime := time.Now()
	r.calculateAreaWeight()
	areaSettles := r.calculateKillerShowArea()
	killerCount := r.calculateKillerCount(r.RoomConfig.KillerMin, r.RoomConfig.KillerMax)
	if killerCount >= areaSettles.Size() {
		killerCount = areaSettles.Size() - 1
	}
	killerAreas := datastructure.NewList([]int{})
	if r.RoomConfig.ForwardProfit == 1 {
		for i := 0; i < killerCount; i++ {
			killerArea, index := r.calculateForwardProfit(areaSettles.Data())
			killerAreas.Push(killerArea)
			areaSettles.DeleteAt(index)
		}
	} else {
		for i := 0; i < killerCount; i++ {
			killerArea, index := r.weightRandom(areaSettles.Data())
			global.Logger.Infof("killerArea:%v, index:%v", killerArea, index)
			killerAreas.Push(killerArea)
			areaSettles.DeleteAt(index)
		}
	}
	r.LastKillerAreas = killerAreas
	global.Logger.Infof("杀手出现的区域:%v", killerAreas)
	r.calculatePlayerProfitLoss(killerAreas)
	r.calculateRoomTax()
	r.calculateRoomProfitLoss()
	r.generatePlayerSettle(killerAreas, settleTime)
	r.saveAreaWeight()
	playerCount := 0
	r.BetInfos.Range(func(k int64, v *BetInfo) bool {
		info, ok := r.PlayerInfos.Get(v.PlayerId)
		if ok {
			if !info.IsRobot {
				playerCount++
			}
		}
		return true
	})
	if playerCount != 0 {
		r.savePeriods(killerAreas, settleTime)
	}
	global.Logger.Infof("房间:%v结算信息生成完成", r.RoomConfig.RoomLevel)
}

func (r *RoomInfo) calculateAreaWeight() {
	r.BetInfos.Range(func(k int64, v *BetInfo) bool {
		playerInfo, _ := r.PlayerInfos.Get(v.PlayerId)
		if playerInfo.IsRobot {
			r.AreaSettles[v.RoomArea].RobotBetCoins += v.BetCoins
		} else {
			r.AreaSettles[v.RoomArea].PlayerBetCoins += v.BetCoins
			weight := int64(0)
			if playerInfo.WinPercent != 0 {
				weight = playerInfo.WinPercent
			} else {
				if r.RoomConfig.RoomGainLoss > r.RoomConfig.RoomTax {
					weight = r.RoomConfig.WinUpPercent
				} else {
					weight = -r.RoomConfig.WinDownPercent
				}
			}
			global.Logger.Infof("playerInfo.WinPercent%v", weight)
			currentWeight := r.AreaSettles[v.RoomArea].Weight
			if weight > 0 {
				if weight > 10000 {
					weight = 10000
				}
				currentWeight = int64(float64(currentWeight) * (1 - float64(weight)/10000))

			} else {
				global.Logger.Infof("weight:%v", weight)
				global.Logger.Infof("weight <= -10000:%v", int64(float64(currentWeight)/(1+float64(weight)/10000)))
				if weight <= -10000 {
					currentWeight = 9999999
				} else {
					currentWeight = int64(float64(currentWeight) / (1 + float64(weight)/10000))
				}
				global.Logger.Infof("weight <= -10000:%v", currentWeight)

			}

			r.AreaSettles[v.RoomArea].Weight = currentWeight
			if r.AreaSettles[v.RoomArea].Weight < 0 {
				r.AreaSettles[v.RoomArea].Weight = 0
			}
		}
		return true
	})
	global.Logger.Infof("房间:%v计算区域权重%v", r.RoomConfig.RoomLevel, convertor.ToString(r.AreaSettles))
}

func (r *RoomInfo) calculateKillerCount(min int, max int) int {
	choices := make([]weightedrand.Choice[int, int], 0)
	for i := min; i <= max; i++ {
		choices = append(choices, weightedrand.NewChoice(i, 1))
	}
	chooser, _ := weightedrand.NewChooser[int, int](choices...)
	count := chooser.Pick()
	return count
}

func (r *RoomInfo) calculateKillerShowArea() *datastructure.List[*AreaSettle] {
	areaResults := strings.Split(r.RoomConfig.SimulationLottery, ",")
	killerAreas := make([]int, 0)
	for k, v := range areaResults {
		result, _ := convertor.ToBool(v)
		if result {
			killerAreas = append(killerAreas, k+1)
		}
	}

	areaSettles := datastructure.NewList([]*AreaSettle{})

	for _, v := range killerAreas {
		areaSettles.Push(r.AreaSettles[v])
	}
	global.Logger.Infof("房间:%v计算杀手区域%v", r.RoomConfig.RoomLevel, convertor.ToString(areaSettles))
	return areaSettles
}

func (r *RoomInfo) calculateKillerWay(killerAreas *datastructure.List[int]) []string {
	killerWays := make([]string, 0)
	for i := 0; i < killerAreas.Size(); i++ {
		chooser, _ := weightedrand.NewChooser(
			weightedrand.NewChoice(2, 1),
			weightedrand.NewChoice(3, 1),
		)
		killerWay := make([]string, 0)
		wayCount := chooser.Pick()
		for i := 0; i < wayCount; i++ {
			way := random.RandInt(1, 9)
			killerWay = append(killerWay, convertor.ToString(way))
		}
		killerWays = append(killerWays, strings.Join(killerWay, ","))
	}
	return killerWays
}

func (r *RoomInfo) weightRandom(areaSettles []*AreaSettle) (int, int) {
	choices := make([]weightedrand.Choice[int, int64], 0)
	KillWeight := make([]*AreaSettle, 0)
	// totalWeight := int64(0)
	for _, v := range areaSettles {
		if v.Weight == 9999999 {
			KillWeight = append(KillWeight, v)
		}
	}
	killerArea := 0

	if len(KillWeight) != 0 {
		index := random.RandInt(0, len(KillWeight))
		killerArea = KillWeight[index].Type
	} else {

		for _, v := range areaSettles {
			// if totalWeight-v.Weight == 0 {
			choices = append(choices, weightedrand.NewChoice(v.Type, v.Weight))
			// } else {
			// choices = append(choices, weightedrand.NewChoice(v.Type, totalWeight-v.Weight))
			// }
		}
		chooser, _ := weightedrand.NewChooser[int, int64](choices...)
		killerArea = chooser.Pick()

	}

	index := -1
	for k, v := range areaSettles {
		if v.Type == killerArea {
			index = k
		}
	}
	return killerArea, index
}

func (r *RoomInfo) calculateForwardProfit(areaSettles []*AreaSettle) (int, int) {
	showAreas := make([]*AreaSettle, 0)
	killerArea := 1
	dif := int64(0)
	for _, v := range areaSettles {
		if v.PlayerBetCoins > 0 {
			showAreas = append(showAreas, v)
		}
	}
	if len(showAreas) == 0 {
		killerArea = areaSettles[random.RandInt(0, len(areaSettles))].Type
	} else {
		for k, v := range showAreas {
			if k == 0 {
				dif = v.PlayerBetCoins - v.RobotBetCoins
				killerArea = v.Type
			} else {
				temp := v.PlayerBetCoins - v.RobotBetCoins
				if temp > dif {
					dif = temp
					killerArea = v.Type
				}
			}
		}
	}
	index := -1
	for k, v := range areaSettles {
		if v.Type == killerArea {
			index = k
		}
	}
	return killerArea, index
}

func (r *RoomInfo) calculatePlayerProfitLoss(killerAreas *datastructure.List[int]) {
	loseCoins := int64(0)
	winCoins := int64(0)
	difCoins := int64(0)
	for k, v := range r.AreaSettles {
		if killerAreas.Contain(k) {
			loseCoins += v.PlayerBetCoins
			loseCoins += v.RobotBetCoins
		} else {
			winCoins += v.PlayerBetCoins
			winCoins += v.RobotBetCoins
		}
	}
	difCoins = loseCoins - winCoins
	if difCoins > 0 {
		difCoins = winCoins
	} else {
		difCoins = loseCoins
	}
	r.BetInfos.Range(func(k int64, v *BetInfo) bool {
		if v.IsRobot {
			return true
		}
		if killerAreas.Contain(v.RoomArea) {
			return true
		}
		v.GameResult = WIN
		playerWinCoins := difCoins * v.BetCoins / winCoins
		v.WinCoins = playerWinCoins
		v.ProfitLossCoins = playerWinCoins * (int64(10000) - r.RoomConfig.PumpWaterPercent) / int64(10000)
		return true
	})
	r.BetInfos.Range(func(k int64, v *BetInfo) bool {
		if v.IsRobot {
			return true
		}
		if !killerAreas.Contain(v.RoomArea) {
			return true
		}
		v.GameResult = LOSE
		playerWinCoins := (loseCoins - difCoins) * v.BetCoins / loseCoins
		v.WinCoins = playerWinCoins
		v.ProfitLossCoins = playerWinCoins - v.BetCoins
		return true
	})
}

func (r *RoomInfo) calculateRoomTax() {
	r.BetInfos.Range(func(k int64, v *BetInfo) bool {
		if v.IsRobot {
			return true
		}
		playerInfo, ok := r.PlayerInfos.Get(v.PlayerId)
		if !ok {
			return true
		}
		if playerInfo.UserType == DEMO {
			return true
		}
		if v.GameResult == WIN {
			r.RoomConfig.RoomTax += v.WinCoins * r.RoomConfig.TaxPercent / int64(10000)
		}
		return true
	})
}

func (r *RoomInfo) calculateRoomProfitLoss() {
	r.BetInfos.Range(func(k int64, v *BetInfo) bool {
		if v.IsRobot {
			return true
		}
		playerInfo, ok := r.PlayerInfos.Get(v.PlayerId)
		if !ok {
			return true
		}
		if playerInfo.UserType == DEMO {
			return true
		}
		r.RoomConfig.RoomGainLoss += -v.ProfitLossCoins
		return true
	})
}

func (r *RoomInfo) generatePlayerSettle(killerAreas *datastructure.List[int], settleTime time.Time) {
	killerWays := r.calculateKillerWay(killerAreas)
	killerAreasStr := make([]string, 0)
	for _, v := range killerAreas.Data() {
		killerAreasStr = append(killerAreasStr, convertor.ToString(v))
	}

	index := int64(0)
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			return true
		}
		settleInfoRes := &proto.SettleInfoRes{
			SettleTime:      tool.CurrentTimestamp(),
			RoomId:          r.RoomConfig.Id,
			Periods:         r.RoomConfig.Periods,
			RoomLevel:       r.RoomConfig.RoomLevel,
			RoomArea:        0,
			KillerAreas:     strings.Join(killerAreasStr, ","),
			KillerWays:      killerWays,
			PlayerId:        k,
			BetCoins:        0,
			ProfitLossCoins: 0,
			GameResult:      NO,
		}
		betInfo, ok := r.BetInfos.Get(k)
		if ok {
			settleInfoRes.GameResult = betInfo.GameResult
			settleInfoRes.RoomArea = betInfo.RoomArea
			settleInfoRes.BetCoins = betInfo.BetCoins
			settleInfoRes.ProfitLossCoins = betInfo.ProfitLossCoins
			index++
			settleEntity := entity.SettleEntity{
				Id:              tool.Snowflake(index),
				CreatedAt:       time.Now(),
				UpdatedAt:       time.Now(),
				Status:          1,
				SettleTime:      settleTime,
				RoomId:          betInfo.RoomId,
				RoomLevel:       betInfo.RoomLevel,
				RoomPeriods:     betInfo.RoomPeriods,
				RoomArea:        betInfo.RoomArea,
				KillerAreas:     strings.Join(killerAreasStr, ","),
				UserId:          v.Id,
				IsRobot:         v.IsRobot,
				NickName:        v.NickName,
				Level:           v.Level,
				BetCoins:        betInfo.BetCoins,
				ProfitLossCoins: betInfo.ProfitLossCoins,
				PumpWaterCoins:  0,
				GameResult:      betInfo.GameResult,
				GameType:        r.RoomConfig.GameType,
				Details:         make([]entity.SettleEntity, 0),
			}
			if betInfo.GameResult == WIN {
				settleEntity.PumpWaterCoins = betInfo.WinCoins - betInfo.ProfitLossCoins
			}
			if betInfo.GameResult == WIN && settleEntity.ProfitLossCoins >= r.RoomConfig.BroadcastProfit {
				r.winBroadcast(settleEntity.NickName, settleEntity.ProfitLossCoins, settleEntity.RoomLevel)
			}
			settleInfoJson, _ := convertor.ToJson(settleEntity)
			global.RedisClient.LPush(context.Background(), viper.GetString("redis.settle-key"), settleInfoJson)
			// r.generatePlayerSettleCount(v.Id)
			settle.SaveOne(&settleEntity)
			v.Coins = v.Coins + betInfo.BetCoins + betInfo.ProfitLossCoins
			playerInfoJson, _ := convertor.ToJson(v)
			tokenTime := viper.GetInt("redis.token-time")
			global.RedisClient.Set(context.Background(), viper.GetString("redis.player-key")+convertor.ToString(v.Id), playerInfoJson, time.Duration(tokenTime)*time.Minute)
			global.Logger.Infof("机器人:%v,玩家ID:%v,昵称:%v,当前持有金币:%v", v.IsRobot, v.Id, v.NickName, v.Coins)

			r.AddStringToHashArray(context.Background(), global.RedisClient, viper.GetString("redis.player-settle-map"), v.UserCode, strconv.FormatInt(settleEntity.Id, 10))
		}

		messageWrapper := &message.MessageWrapper{
			Code: proto.SETTLE,
			Data: settleInfoRes,
		}

		connectionWrapper := connection.ConnectionManagerInstance().QueryConnOne(k)
		if connectionWrapper != nil {
			connectionWrapper.Receive(messageWrapper)
		}

		return true
	})
}

func (r *RoomInfo) AddStringToHashArray(ctx context.Context, rdb *redis.Client, key, field, value string) error {
	// 获取当前字段的值
	existing, err := rdb.HGet(ctx, key, field).Result()
	if err != nil && err != redis.Nil {
		return err
	}

	// 反序列化为字符串数组
	var arr []string
	if existing != "" {
		err = json.Unmarshal([]byte(existing), &arr)
		if err != nil {
			global.Logger.Infof("error unmarshaling JSON: %v", err)
		}
	}

	// 添加新字符串到数组
	arr = append(arr, value)

	// 序列化数组回 JSON 字符串
	updated, err := json.Marshal(arr)
	if err != nil {
		global.Logger.Infof("error marshaling JSON: %v", err)
	}

	global.Logger.Infof("error marshaling JSON: %v %v %v", key, field, string(updated))

	// 更新 Redis 中的值
	return rdb.HSet(ctx, key, field, string(updated)).Err()
}

func (r *RoomInfo) generatePlayerSettleCount(playerId int64) {
	global.Logger.Infof("生成玩家结算次数,玩家ID:%v", playerId)
	global.RedisClient.Incr(context.Background(), viper.GetString("redis.player-settle-count-key")+convertor.ToString(playerId))
}

func (r *RoomInfo) saveAreaWeight() {
	areaWeightEntities := make([]*entity.AreaWeightEntity, 0)
	lastKillerAreasStr := make([]string, 0)
	r.LastKillerAreas.ForEach(func(v int) {
		lastKillerAreasStr = append(lastKillerAreasStr, convertor.ToString(v))
	})
	for _, v := range r.AreaSettles {
		areaWeightEntity := &entity.AreaWeightEntity{
			RoomId:      r.RoomConfig.Id,
			RoomLevel:   r.RoomConfig.RoomLevel,
			RoomPeriods: r.RoomConfig.Periods,
			RoomArea:    v.Type,
			KillerAreas: strings.Join(lastKillerAreasStr, ","),
			Weight:      v.Weight,
			GameType:    r.RoomConfig.GameType,
		}
		areaWeightEntities = append(areaWeightEntities, areaWeightEntity)
	}
	if len(areaWeightEntities) != 0 {
		areaweight.SaveBatch(areaWeightEntities)
	}
}

func (r *RoomInfo) savePeriods(killerAreas *datastructure.List[int], settleTime time.Time) {
	killerAreasStr := make([]string, 0)
	for _, v := range killerAreas.Data() {
		killerAreasStr = append(killerAreasStr, convertor.ToString(v))
	}
	index := int64(0)
	for _, v := range r.AreaSettles {
		index++
		periodEntity := entity.PeriodEntity{
			Id:          tool.Snowflake(index),
			CreatedAt:   time.Now(),
			UpdatedAt:   time.Now(),
			Status:      1,
			SettleTime:  settleTime,
			RoomId:      r.RoomConfig.Id,
			RoomLevel:   r.RoomConfig.RoomLevel,
			RoomPeriods: r.RoomConfig.Periods,
			KillerAreas: strings.Join(killerAreasStr, ","),
			RoomArea:    v.Type,
			PlayerCoins: v.PlayerBetCoins,
			RobotCoins:  v.RobotBetCoins,
			GameType:    r.RoomConfig.GameType,
		}
		periodInfoJson, _ := convertor.ToJson(periodEntity)
		global.RedisClient.LPush(context.Background(), viper.GetString("redis.period-key"), periodInfoJson)
		period.SaveOne(&periodEntity)
	}
}

func (r *RoomInfo) generateRobot() {
	playerIds := make([]int64, 0)
	robotIds := make([]int64, 0)
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			robotIds = append(robotIds, v.Id)
		} else {
			playerIds = append(playerIds, v.Id)
		}
		return true
	})
	robotCount := 0
	r.RobotInfos.Range(func(k int64, v *RobotInfo) bool {
		robotCount++
		return true
	})
	totalCount := random.RandInt(r.RoomConfig.MinRobotsCount, r.RoomConfig.MaxRobotsCount+1)
	realCount := totalCount - robotCount
	if realCount > 0 {
		for i := 0; i < realCount; i++ {
			robotInfo := &RobotInfo{
				Id:       tool.Snowflake(int64(i)),
				NickName: random.RandString(6),
				BetCoins: r.robotBetCoins(),
				BetArea:  random.RandInt(1, len(r.AreaInfos)+1),
				BetTime:  random.RandInt(r.RoomConfig.StartTimeRoom, r.RoomConfig.EndTimeRoom+1),
				IsGaming: false,
				IsBet:    r.robotIsBet(),
			}
			r.RobotInfos.Set(robotInfo.Id, robotInfo)
		}
	} else {
		for i := 0; i < 0-realCount; i++ {
			r.RobotInfos.Delete(robotIds[i])
			r.PlayerInfos.Delete(robotIds[i])
			connectionWrappers := connection.ConnectionManagerInstance().QueryConnMultiple(playerIds)
			for _, ele := range connectionWrappers {
				if ele == nil {
					continue
				}
				messageWrapper := &message.MessageWrapper{
					Code: proto.ROOMEXIT,
					Data: robotIds[i],
				}
				ele.Receive(messageWrapper)
			}
		}
	}
}

func (r *RoomInfo) robotComplete() {
	count := 0
	r.RobotInfos.Range(func(k int64, v *RobotInfo) bool {
		count++
		return true
	})
	if count == 0 {
		global.Logger.Infof("房间:%v生成机器人%v个", r.RoomConfig.RoomLevel, count)
		return
	}
	r.RobotInfos.Range(func(k int64, v *RobotInfo) bool {
		v.IsBet = r.robotIsBet()
		info, ok := r.PlayerInfos.Get(v.Id)
		if ok {
			info.Coins = v.BetCoins
			info.WinPercent = int64(0)
			info.LastBetTimeStamp = tool.CurrentTimestamp()
		} else {
			playerInfo := &PlayerInfo{
				Id:               v.Id,
				NickName:         v.NickName,
				Coins:            v.BetCoins,
				WinPercent:       int64(0),
				LastBetTimeStamp: tool.CurrentTimestamp(),
				IsGaming:         false,
				IsRobot:          true,
			}
			r.PlayerInfos.Set(v.Id, playerInfo)
		}
		return true
	})
	r.SynPlayerInfo()
	robotCount := 0
	r.RobotInfos.Range(func(k int64, v *RobotInfo) bool {
		robotCount++
		return true
	})
	global.Logger.Infof("房间:%v生成机器人%v个", r.RoomConfig.RoomLevel, robotCount)
}

func (r *RoomInfo) robotBet() {
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if !v.IsRobot {
			return true
		}
		if v.IsGaming {
			return true
		}
		if v.Coins <= 0 {
			return true
		}
		robotInfo, _ := r.RobotInfos.Get(v.Id)
		if !robotInfo.IsBet {
			return true
		}
		if r.Countdown > (60 - robotInfo.BetTime) {
			return true
		}
		v.IsGaming = true
		betInfo := &BetInfo{
			RoomId:          r.RoomConfig.Id,
			RoomLevel:       r.RoomConfig.RoomLevel,
			RoomPeriods:     r.RoomConfig.Periods,
			RoomArea:        robotInfo.BetArea,
			PlayerId:        v.Id,
			NickName:        v.NickName,
			BetCoins:        v.Coins,
			IsRobot:         true,
			WinCoins:        0,
			ProfitLossCoins: 0,
			GameResult:      NO,
		}
		r.BetInfos.Set(v.Id, betInfo)
		v.Coins = 0
		return true
	})
}

func (r *RoomInfo) robotPartExitRoom() {
	playerIds := make([]int64, 0)
	robotIds := make([]int64, 0)
	r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
		if v.IsRobot {
			v.IsGaming = false
			robotIds = append(robotIds, v.Id)
		} else {
			playerIds = append(playerIds, v.Id)
		}
		return true
	})
	if len(robotIds) == 0 {
		return
	}
	for _, v := range robotIds {
		chooser, _ := weightedrand.NewChooser(
			weightedrand.NewChoice(true, 1),
			weightedrand.NewChoice(false, 3),
		)
		isExit := chooser.Pick()
		if isExit {
			r.PlayerInfos.Delete(v)
			r.RobotInfos.Delete(v)
			connectionWrappers := connection.ConnectionManagerInstance().QueryConnMultiple(playerIds)
			for _, ele := range connectionWrappers {
				if ele == nil {
					continue
				}
				messageWrapper := &message.MessageWrapper{
					Code: proto.ROOMEXIT,
					Data: v,
				}
				ele.Receive(messageWrapper)
			}
		}
	}
}

func (r *RoomInfo) robotIsBet() bool {
	trueWeight := r.RoomConfig.BetWeight
	if trueWeight > 10000 {
		trueWeight = 10000
	}
	chooser, _ := weightedrand.NewChooser[bool, int64](
		weightedrand.NewChoice(true, trueWeight),
		weightedrand.NewChoice(false, 10000-trueWeight),
	)
	result := chooser.Pick()
	return result
}

func (r *RoomInfo) robotBetCoins() int64 {
	betCoins := int64(0)
	if r.RoomConfig.BetItem == "" {
		return betCoins
	}
	choices := make([]weightedrand.Choice[int64, int64], 0)
	coinsWeights := strings.Split(r.RoomConfig.BetItem, ",")
	for _, v := range coinsWeights {
		item := strings.Split(v, "|")
		coins, _ := convertor.ToInt(item[0])
		weight, _ := convertor.ToInt(item[1])
		if betCoins > r.RoomConfig.MaxGold {
			continue
		}
		choices = append(choices, weightedrand.NewChoice(coins, weight))
	}
	if len(choices) == 0 {
		return betCoins
	}
	chooser, _ := weightedrand.NewChooser[int64, int64](choices...)
	betCoins = chooser.Pick()
	return betCoins
}

func (r *RoomInfo) robotInOrOut() {
	chooser, _ := weightedrand.NewChooser(
		weightedrand.NewChoice(-1, 10),
		weightedrand.NewChoice(0, 1),
		weightedrand.NewChoice(1, 1),
	)
	flag := chooser.Pick()
	robotInCount := 1
	switch flag {
	case 0:
		playerIds := make([]int64, 0)
		noGameRobotIds := make([]int64, 0)
		r.PlayerInfos.Range(func(k int64, v *PlayerInfo) bool {
			if v.IsRobot {
				if !v.IsGaming {
					noGameRobotIds = append(noGameRobotIds, v.Id)
				}
			} else {
				playerIds = append(playerIds, v.Id)
			}
			return true
		})
		if len(noGameRobotIds) != 0 {
			robotId := noGameRobotIds[0]
			r.PlayerInfos.Delete(robotId)
			r.RobotInfos.Delete(robotId)
			connectionWrappers := connection.ConnectionManagerInstance().QueryConnMultiple(playerIds)
			for _, v := range connectionWrappers {
				if v == nil {
					continue
				}
				messageWrapper := &message.MessageWrapper{
					Code: proto.ROOMEXIT,
					Data: robotId,
				}
				v.Receive(messageWrapper)
			}
		}
		break
	case 1:
		for i := 0; i < robotInCount; i++ {
			robotInfo := &RobotInfo{
				Id:       tool.Snowflake(int64(robotInCount)),
				NickName: random.RandString(6),
				BetCoins: r.robotBetCoins(),
				BetArea:  random.RandInt(1, len(r.AreaInfos)+1),
				BetTime:  random.RandInt(r.RoomConfig.StartTimeRoom, r.RoomConfig.EndTimeRoom+1),
				IsGaming: false,
				IsBet:    false,
			}
			playerInfo := &PlayerInfo{
				Id:               robotInfo.Id,
				NickName:         robotInfo.NickName,
				Coins:            robotInfo.BetCoins,
				WinPercent:       int64(0),
				LastBetTimeStamp: tool.CurrentTimestamp(),
				IsGaming:         false,
				IsRobot:          true,
			}
			r.RobotInfos.Set(robotInfo.Id, robotInfo)
			r.PlayerInfos.Set(robotInfo.Id, playerInfo)
		}
		break
	case -1:
		break
	default:
		break
	}
}

func (r *RoomInfo) winBroadcast(nickName string, winCoins int64, roomLevel int) {
	winBroadcastRes := &proto.WinBroadcastRes{
		NickName:  nickName[0:1] + "**",
		WinCoins:  winCoins,
		RoomLevel: roomLevel,
	}
	messageWrapper := &message.MessageWrapper{
		Code: proto.WINBROADCAST,
		Data: winBroadcastRes,
	}
	connAll := connection.ConnectionManagerInstance().QueryConnAll()
	connAll.Range(func(key int64, conn *connection.ConnectionWrapper) bool {
		conn.Receive(messageWrapper)
		return true
	})
}

func (r *RoomInfo) prizePoolIncrease() {
	prizeCoins := random.RandInt(int(r.RoomConfig.AutoMinAmount), int(r.RoomConfig.AutoMaxAmount+1))
	r.PrizePoolCoins += int64(prizeCoins)
}

func (r *RoomInfo) prizeSettle() {
	if r.RoomConfig.State == 2 {
		return
	}
	if r.PrizePoolCoins < r.RoomConfig.AutoEruptAmount {
		return
	}
	r.PrizeToAll(-1)
}

func (r *RoomInfo) PrizeManualSignal() {
	global.Logger.Infof("房间%v开始接受手动开奖信号", r.RoomConfig.RoomLevel)
	key := viper.GetString("redis.prize-open-key") + viper.GetString("environment.game-type") + "_" + convertor.ToString(r.RoomConfig.RoomLevel)
	for {
		isExists := global.RedisClient.Exists(context.Background(), key).Val()
		if isExists > 0 {
			r.HandlePrizeOpen(key)
		}
		time.Sleep(1 * time.Second)
	}
}

func (r *RoomInfo) HandlePrizeOpen(key string) {
	defer r.PrizeLock.Unlock()
	r.PrizeLock.Lock()
	global.Logger.Infof("接受到手动开奖信号,key:%v", key)
	prizeOpenInfoRd := global.RedisClient.Get(context.Background(), key).Val()
	playerRdInfo := &PrizeOpenInfo{}
	json.Unmarshal([]byte(prizeOpenInfoRd), playerRdInfo)
	global.Logger.Infof("手动开奖信息:%v", playerRdInfo)
	switch playerRdInfo.Type {
	case TOROBOT:
		r.PrizeToRobot(playerRdInfo.Count)
		break
	case TOPLAYER:
		r.PrizeToPlayer(playerRdInfo.Count)
		break
	case TOALL:
		r.PrizeToAll(playerRdInfo.Count)
		break
	default:
		break
	}
	global.RedisClient.Del(context.Background(), key)
	r.SynPrizeInfo()
}

func (r *RoomInfo) PrizeToPlayer(targetCount int64) {
	if targetCount == -1 {
		prizeCount := int(r.RoomConfig.EruptCount)
		flag := false
		if r.PrizePoolCoins >= r.RoomConfig.AutoEruptAmount {
			flag = true
		}
		if !flag {
			return
		}
		playerIds := datastructure.NewList([]int64{})
		r.PlayerInfos.Range(func(key int64, value *PlayerInfo) bool {
			if value.IsRobot {
				return true
			}
			playerIds.Push(value.Id)
			return true
		})
		if prizeCount > playerIds.Size() {
			prizeCount = playerIds.Size()
		}
		if prizeCount == 0 {
			return
		}
		prizeSettles := make([]*entity.PrizeEntity, 0)
		for i := 0; i < prizeCount; i++ {
			index := random.RandInt(0, playerIds.Size())
			indexOf, ok := playerIds.ValueOf(index)
			if !ok {
				continue
			}
			playerInfo, ok := r.PlayerInfos.Get(*indexOf)
			if !ok {
				continue
			}
			prizeCoin := random.RandInt(int(r.RoomConfig.EruptMinAmount), int(r.RoomConfig.EruptMaxAmount+1))
			prizeEntity := &entity.PrizeEntity{
				Id:         tool.Snowflake(int64(i)),
				CreatedAt:  time.Now(),
				UpdatedAt:  time.Now(),
				Status:     1,
				SettleTime: time.Now(),
				RoomId:     r.RoomConfig.Id,
				RoomLevel:  r.RoomConfig.RoomLevel,
				UserId:     playerInfo.Id,
				IsRobot:    playerInfo.IsRobot,
				NickName:   playerInfo.NickName,
				PrizeCoins: int64(prizeCoin),
				GameType:   r.RoomConfig.GameType,
			}
			prizeSettles = append(prizeSettles, prizeEntity)
			playerIds.DeleteAt(index)
		}
		prizeTotal := int64(0)
		for _, v := range prizeSettles {
			prizeTotal += v.PrizeCoins
		}
		prize.SaveBatch(prizeSettles)
		for _, v := range prizeSettles {
			settleInfoJson, _ := convertor.ToJson(v)
			global.RedisClient.LPush(context.Background(), viper.GetString("redis.prize-key"), settleInfoJson)
			playerInfo, ok := r.PlayerInfos.Get(v.UserId)
			if !ok {
				continue
			}
			if playerInfo.UserType != DEMO {
				r.RoomConfig.RoomGainLoss -= v.PrizeCoins
			}
			playerInfo.Coins += v.PrizeCoins
			jsonStr, _ := convertor.ToJson(playerInfo)
			tokenTime := viper.GetInt("redis.token-time")
			global.RedisClient.Set(context.Background(), viper.GetString("redis.player-key")+convertor.ToString(playerInfo.Id), jsonStr, time.Duration(tokenTime)*time.Minute)
		}
		r.prizeOpenBroadcast(prizeSettles)
		r.PrizePoolCoins = r.PrizePoolCoins - prizeTotal
		if r.PrizePoolCoins < 0 {
			r.PrizePoolCoins = 0
		}
	} else {
		prizeCount := int(r.RoomConfig.EruptCount)
		playerIds := datastructure.NewList([]int64{})
		r.PlayerInfos.Range(func(key int64, value *PlayerInfo) bool {
			if value.IsRobot {
				return true
			}
			playerIds.Push(value.Id)
			return true
		})
		if prizeCount > playerIds.Size() {
			prizeCount = playerIds.Size()
		}
		if prizeCount == 0 {
			return
		}
		prizeSettles := make([]*entity.PrizeEntity, 0)
		for i := 0; i < prizeCount; i++ {
			index := random.RandInt(0, playerIds.Size())
			indexOf, ok := playerIds.ValueOf(index)
			if !ok {
				continue
			}
			playerInfo, ok := r.PlayerInfos.Get(*indexOf)
			if !ok {
				continue
			}
			prizeCoin := targetCount / int64(prizeCount)
			prizeEntity := &entity.PrizeEntity{
				Id:         tool.Snowflake(int64(i)),
				CreatedAt:  time.Now(),
				UpdatedAt:  time.Now(),
				Status:     1,
				SettleTime: time.Now(),
				RoomId:     r.RoomConfig.Id,
				RoomLevel:  r.RoomConfig.RoomLevel,
				UserId:     playerInfo.Id,
				IsRobot:    playerInfo.IsRobot,
				NickName:   playerInfo.NickName,
				PrizeCoins: prizeCoin,
				GameType:   r.RoomConfig.GameType,
			}
			prizeSettles = append(prizeSettles, prizeEntity)
			playerIds.DeleteAt(index)
		}
		prize.SaveBatch(prizeSettles)
		for _, v := range prizeSettles {
			settleInfoJson, _ := convertor.ToJson(v)
			global.RedisClient.LPush(context.Background(), viper.GetString("redis.prize-key"), settleInfoJson)
			playerInfo, ok := r.PlayerInfos.Get(v.UserId)
			if !ok {
				continue
			}
			if playerInfo.UserType != DEMO {
				r.RoomConfig.RoomGainLoss -= v.PrizeCoins
			}
			playerInfo.Coins += v.PrizeCoins
			jsonStr, _ := convertor.ToJson(playerInfo)
			tokenTime := viper.GetInt("redis.token-time")
			global.RedisClient.Set(context.Background(), viper.GetString("redis.player-key")+convertor.ToString(playerInfo.Id), jsonStr, time.Duration(tokenTime)*time.Minute)
		}
		r.prizeOpenBroadcast(prizeSettles)
		r.PrizePoolCoins = r.PrizePoolCoins - targetCount
		if r.PrizePoolCoins < 0 {
			r.PrizePoolCoins = 0
		}
	}
}

func (r *RoomInfo) PrizeToRobot(targetCount int64) {
	if targetCount == -1 {
		prizeCount := int(r.RoomConfig.EruptCount)
		prizeTotal := int64(0)
		for i := 0; i < prizeCount; i++ {
			prizeCoin := random.RandInt(int(r.RoomConfig.EruptMinAmount), int(r.RoomConfig.EruptMaxAmount+1))
			prizeTotal += int64(prizeCoin)
		}
		r.PrizePoolCoins = r.PrizePoolCoins - prizeTotal
		if r.PrizePoolCoins < 0 {
			r.PrizePoolCoins = 0
		}
		prizeSettles := make([]*entity.PrizeEntity, 0)
		prizeEntity := &entity.PrizeEntity{
			Id:         tool.Snowflake(1),
			CreatedAt:  time.Now(),
			UpdatedAt:  time.Now(),
			Status:     1,
			SettleTime: time.Now(),
			RoomId:     r.RoomConfig.Id,
			RoomLevel:  r.RoomConfig.RoomLevel,
			UserId:     tool.Snowflake(1),
			IsRobot:    true,
			NickName:   "a****",
			PrizeCoins: prizeTotal,
			GameType:   r.RoomConfig.GameType,
		}
		prizeSettles = append(prizeSettles, prizeEntity)
		r.prizeOpenBroadcast(prizeSettles)
	} else {
		r.PrizePoolCoins = r.PrizePoolCoins - targetCount
		if r.PrizePoolCoins < 0 {
			r.PrizePoolCoins = 0
		}
		prizeSettles := make([]*entity.PrizeEntity, 0)
		prizeEntity := &entity.PrizeEntity{
			Id:         tool.Snowflake(1),
			CreatedAt:  time.Now(),
			UpdatedAt:  time.Now(),
			Status:     1,
			SettleTime: time.Now(),
			RoomId:     r.RoomConfig.Id,
			RoomLevel:  r.RoomConfig.RoomLevel,
			UserId:     tool.Snowflake(1),
			IsRobot:    true,
			NickName:   "a****",
			PrizeCoins: targetCount,
			GameType:   r.RoomConfig.GameType,
		}
		prizeSettles = append(prizeSettles, prizeEntity)
		r.prizeOpenBroadcast(prizeSettles)
	}
}

func (r *RoomInfo) PrizeToAll(targetCount int64) {
	if targetCount == -1 {
		prizeCount := int(r.RoomConfig.EruptCount)
		targetIds := datastructure.NewList([]int64{})
		r.PlayerInfos.Range(func(key int64, value *PlayerInfo) bool {
			targetIds.Push(value.Id)
			return true
		})
		if prizeCount > targetIds.Size() {
			prizeCount = targetIds.Size()
		}
		if prizeCount == 0 {
			return
		}
		prizeSettles := make([]*entity.PrizeEntity, 0)
		for i := 0; i < prizeCount; i++ {
			index := random.RandInt(0, targetIds.Size())
			indexOf, ok := targetIds.ValueOf(index)
			if !ok {
				continue
			}
			playerInfo, ok := r.PlayerInfos.Get(*indexOf)
			if !ok {
				continue
			}
			prizeCoin := random.RandInt(int(r.RoomConfig.EruptMinAmount), int(r.RoomConfig.EruptMaxAmount+1))
			prizeEntity := &entity.PrizeEntity{
				Id:         tool.Snowflake(int64(i)),
				CreatedAt:  time.Now(),
				UpdatedAt:  time.Now(),
				Status:     1,
				SettleTime: time.Now(),
				RoomId:     r.RoomConfig.Id,
				RoomLevel:  r.RoomConfig.RoomLevel,
				UserId:     playerInfo.Id,
				IsRobot:    playerInfo.IsRobot,
				NickName:   playerInfo.NickName,
				PrizeCoins: int64(prizeCoin),
				GameType:   r.RoomConfig.GameType,
			}
			prizeSettles = append(prizeSettles, prizeEntity)
			targetIds.DeleteAt(index)
		}
		prizeTotal := int64(0)
		realPlayerPrizeSettle := make([]*entity.PrizeEntity, 0)
		for _, v := range prizeSettles {
			prizeTotal += v.PrizeCoins
			if v.IsRobot {
				continue
			}
			realPlayerPrizeSettle = append(realPlayerPrizeSettle, v)
		}
		prize.SaveBatch(realPlayerPrizeSettle)
		for _, v := range realPlayerPrizeSettle {
			r.RoomConfig.RoomGainLoss -= v.PrizeCoins
			settleInfoJson, _ := convertor.ToJson(v)
			global.RedisClient.LPush(context.Background(), viper.GetString("redis.prize-key"), settleInfoJson)
			playerInfo, ok := r.PlayerInfos.Get(v.UserId)
			if !ok {
				continue
			}
			if playerInfo.UserType != DEMO {
				r.RoomConfig.RoomGainLoss -= v.PrizeCoins
			}
			playerInfo.Coins += v.PrizeCoins
			jsonStr, _ := convertor.ToJson(playerInfo)
			tokenTime := viper.GetInt("redis.token-time")
			global.RedisClient.Set(context.Background(), viper.GetString("redis.player-key")+convertor.ToString(playerInfo.Id), jsonStr, time.Duration(tokenTime)*time.Minute)
		}
		r.prizeOpenBroadcast(prizeSettles)
		r.PrizePoolCoins = r.PrizePoolCoins - prizeTotal
		if r.PrizePoolCoins < 0 {
			r.PrizePoolCoins = 0
		}
	} else {
		prizeCount := int(r.RoomConfig.EruptCount)
		targetIds := datastructure.NewList([]int64{})
		r.PlayerInfos.Range(func(key int64, value *PlayerInfo) bool {
			targetIds.Push(value.Id)
			return true
		})
		if prizeCount > targetIds.Size() {
			prizeCount = targetIds.Size()
		}
		if prizeCount == 0 {
			return
		}
		prizeSettles := make([]*entity.PrizeEntity, 0)
		for i := 0; i < prizeCount; i++ {
			index := random.RandInt(0, targetIds.Size())
			indexOf, ok := targetIds.ValueOf(index)
			if !ok {
				continue
			}
			playerInfo, ok := r.PlayerInfos.Get(*indexOf)
			if !ok {
				continue
			}
			prizeCoin := targetCount / int64(prizeCount)
			prizeEntity := &entity.PrizeEntity{
				Id:         tool.Snowflake(int64(i)),
				CreatedAt:  time.Now(),
				UpdatedAt:  time.Now(),
				Status:     1,
				SettleTime: time.Now(),
				RoomId:     r.RoomConfig.Id,
				RoomLevel:  r.RoomConfig.RoomLevel,
				UserId:     playerInfo.Id,
				IsRobot:    playerInfo.IsRobot,
				NickName:   playerInfo.NickName,
				PrizeCoins: prizeCoin,
				GameType:   r.RoomConfig.GameType,
			}
			prizeSettles = append(prizeSettles, prizeEntity)
			targetIds.DeleteAt(index)
		}
		realPlayerPrizeSettle := make([]*entity.PrizeEntity, 0)
		for _, v := range prizeSettles {
			if v.IsRobot {
				continue
			}
			realPlayerPrizeSettle = append(realPlayerPrizeSettle, v)
		}
		prize.SaveBatch(realPlayerPrizeSettle)
		for _, v := range realPlayerPrizeSettle {
			r.RoomConfig.RoomGainLoss -= v.PrizeCoins
			settleInfoJson, _ := convertor.ToJson(v)
			global.RedisClient.LPush(context.Background(), viper.GetString("redis.prize-key"), settleInfoJson)
			playerInfo, ok := r.PlayerInfos.Get(v.UserId)
			if !ok {
				continue
			}
			if playerInfo.UserType != DEMO {
				r.RoomConfig.RoomGainLoss -= v.PrizeCoins
			}
			playerInfo.Coins += v.PrizeCoins
			jsonStr, _ := convertor.ToJson(playerInfo)
			tokenTime := viper.GetInt("redis.token-time")
			global.RedisClient.Set(context.Background(), viper.GetString("redis.player-key")+convertor.ToString(playerInfo.Id), jsonStr, time.Duration(tokenTime)*time.Minute)
		}
		r.prizeOpenBroadcast(prizeSettles)
		r.PrizePoolCoins = r.PrizePoolCoins - targetCount
		if r.PrizePoolCoins < 0 {
			r.PrizePoolCoins = 0
		}
	}
}

func (r *RoomInfo) prizeOpenBroadcast(prizeSettles []*entity.PrizeEntity) {
	r.PlayerInfos.Range(func(key int64, value *PlayerInfo) bool {
		if value.IsRobot {
			return true
		}
		prizeSettleRes := proto.PrizeSettleRes{PrizeCoins: 0}
		for _, prizeSettle := range prizeSettles {
			if prizeSettle.UserId == value.Id {
				prizeSettleRes.PrizeCoins = prizeSettle.PrizeCoins
			}
		}
		messageWrapper := &message.MessageWrapper{
			Code: proto.PrizeSettle,
			Data: prizeSettleRes,
		}
		conn := connection.ConnectionManagerInstance().QueryConnOne(value.Id)
		if conn == nil {
			return true
		}
		conn.Receive(messageWrapper)
		return true
	})
}

func (r *RoomInfo) BarrageBroadcast(txt string) {
	words := strings.Split(r.RoomConfig.BarrageWords, ",")
	flag := false
	for _, v := range words {
		if strings.Contains(txt, v) {
			flag = true
			break
		}
	}
	if flag {
		return
	}
	r.PlayerInfos.Range(func(key int64, value *PlayerInfo) bool {
		if value.IsRobot {
			return true
		}
		messageWrapper := &message.MessageWrapper{
			Code: proto.BarrageBroadcast,
			Data: txt,
		}
		conn := connection.ConnectionManagerInstance().QueryConnOne(value.Id)
		if conn == nil {
			return true
		}
		conn.Receive(messageWrapper)
		return true
	})
}

func (r *RoomInfo) barrageLoop() {
	global.Logger.Infof("房间%v开始执行弹幕循环", r.RoomConfig.RoomLevel)
	for {
		if r.RoomConfig.BarrageState != 1 {
			continue
		}
		randomTime := random.RandInt(r.RoomConfig.StartTimeBarrage, r.RoomConfig.EndTimeBarrage+1)
		r.mockBarrage()
		time.Sleep(time.Duration(randomTime) * time.Second)
	}
}

func (r *RoomInfo) mockBarrage() {
	choices := make([]weightedrand.Choice[int, int], 0)
	choices = append(choices, weightedrand.NewChoice(-1, 1))
	for k, _ := range r.BarrageInfos {
		choices = append(choices, weightedrand.NewChoice(k, 1))
	}
	chooser, _ := weightedrand.NewChooser[int, int](choices...)
	pick := chooser.Pick()
	if pick == -1 {
		return
	} else {
		r.BarrageBroadcast(r.BarrageInfos[pick].Content)
	}
}

func (r *RoomInfo) barrageReset() {
	barragesInfos := make([]*Barrage, 0)
	barrages := barrage.QueryAll()
	for _, item := range barrages {
		barrageInfo := Barrage{}
		convertor.CopyProperties(&barrageInfo, &item)
		barragesInfos = append(barragesInfos, &barrageInfo)
	}
	r.BarrageInfos = barragesInfos
}
